﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Xms.Abp.Data;

public interface ITree<TTree>
    where TTree : class, ITree<TTree>
{
    List<TTree> Children { get; set; }
    TTree Clone();

    public ITree<TTree> ForEach(Func<ITree<TTree>, bool> each)
    {
        if (!each(this))
        {
            return this;
        }
        if (Children != null && Children.Count > 0)
        {
            foreach (var child in Children)
            {
                var c = true;
                child.ForEach(t => { c = each(t); return c; });
                if (!c) break;
            }
        }
        return this;
    }

    public ITree<TTree> ForEach(Action<ITree<TTree>> each)
    {
        each(this);
        if (Children != null && Children.Count > 0)
        {
            foreach (var child in Children)
            {
                var c = true;
                child.ForEach(t => each(t));
                if (!c) break;
            }
        }
        return this;
    }

    public bool Contains<TKey>(Func<ITree<TTree>, TKey> keySelector, TKey key)
    {
        var c = false;
        this.ForEach(r =>
        {
            c = keySelector(r).Equals(key);
            return !c;
        });
        return c;
    }

    public ITree<TTree> Filter<TKey>(Func<ITree<TTree>, TKey> keySelector, params TKey[] keys)
    {
        return Filter(keySelector, (IEnumerable<TKey>)keys);
    }

    public ITree<TTree> Filter<TKey>(Func<ITree<TTree>, TKey> keySelector, IEnumerable<TKey> keys)
    {
        if (keys == null || !keys.Any())
        {
            return null;
        }
        return Filter(r => keys.Contains(keySelector(r)));
    }

    public ITree<TTree> Filter(Func<ITree<TTree>, bool> predicate)
    {
        if (predicate(this))
        {
            return this;
        }
        var children = new List<TTree>();
        if (Children == null || Children.Count == 0)
        {
            return null;
        }
        foreach (var child in Children)
        {
            var c = child.Filter(predicate);
            if (c != null)
            {
                children.Add(c as TTree);
            }
        }
        if (children.Count > 0)
        {
            var clone = Clone();
            clone.Children = children;
            return clone;
        }
        else
        {
            return null;
        }
    }
}

public interface ITree<TTree, TKey> : ITree<TTree>
    where TTree : class, ITree<TTree>, ITree<TTree, TKey>
    where TKey : class
{
    TKey Id { get; set; }
    TKey ParentId { get; set; }
}

